/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Threading;

namespace EffiProz.Core.Lib
{
    /**
     * Facility to schedule tasks for future execution _in a background thread.  <p>
     *
     * Tasks may be scheduled for one-time execution or for repeated execution at
     * regular intervals, using either fixed rate or fixed delay policy. <p>
     *
     * This class is a JDK 1.1 compatible implementation required by HSQLDB both
     * because the java.util.Timer class is available only _in JDK 1.3+ and because
     * java.util.Timer starves least recently added tasks under high load and
     * fixed rate scheduling, especially when the average actual task duration is
     * greater than the average requested task periodicity. <p>
     *
     * An additional (minor) advantage over java.util.Timer is that this class does
     * not retain a live background thread during periods when the task queue is
     * empty.
     * @author boucherb@users
     * @version 1.8.0.10
     * @since 1.7.2
     */
    public class EfzTimer : ObjectComparator
    {
        /** The priority queue for the scheduled tasks. */
        protected TaskQueue taskQueue;

        /** The inner runnable that executes tasks in the background thread. */
        protected ThreadStart taskRunner;

        /** The background thread. */
        protected Thread taskRunnerThread;



        /**
         * Whether this timer should disallow all further processing.
         *
         * Once set true, stays true forever.
         */
        protected volatile bool isShutdown;

        /**
         * Constructs a new HsqlTimer using the default thread factory
         * implementation.
         */
        public EfzTimer()            
        {
            taskQueue = new TaskQueue(16,
           (ObjectComparator)this);
            taskRunner = new ThreadStart(run);
        }

       

        /**
         * Required to back the priority queue for scheduled tasks.
         *
         * @param a the first Task
         * @param b the second Task
         * @return 0 if equal, < 0 if a < b, > 0 if a > b
         */
        public int compare(Object a, Object b)
        {

            long awhen = ((Task)(a)).getNextScheduled();
            long bwhen = ((Task)(b)).getNextScheduled();

            return (awhen < bwhen) ? -1
                                   : (awhen == bwhen) ? 0
                                                      : 1;
        }

        /**
         * Default ThreadFactory implementation. <p>
         *
         * Contructs a new Thread from the designated runnable, sets its
         * name to "HSQLDB Timer @" + Integer.toHexString(hashCode()),
         * and sets it as a daemon thread. <p>
         *
         * @param runnable used to construct the new Thread.
         * @return a new Thread constructed from the designated runnable.
         */
        public Thread newThread(ThreadStart runnable)
        {

            Thread thread = new Thread(runnable);

            thread.Name = "EffiProz Timer @" + String.Format("{0:x}", GetHashCode());
            thread.IsBackground = true;

            return thread;
        }

        /**
         * Retrieves the background execution thread. <p>
         *
         * null is returned if there is no such thread. <p>
         *
         * @return the current background thread (may be null)
         */
        public Thread getThread()
        {
            lock (this)
            {
                return this.taskRunnerThread;
            }
        }

        /**
         * (Re)starts background processing of the task queue.
         *
         * @throws IllegalStateException if this timer is shut down.
         * @see #shutdown()
         * @see #shutdownImmediately()
         */
        public void restart()
        {
            lock (this)
            {
                if (this.isShutdown)
                {
                    throw new InvalidOperationException("isShutdown==true");
                }
                else if (this.taskRunnerThread == null)
                {
                    this.taskRunnerThread =
                         new Thread(this.taskRunner);

                    this.taskRunnerThread.Start();
                }
                else
                {
                    this.taskQueue.unpark();
                }
            }
        }

        /**
         * Causes the specified Runnable to be executed once in the background
         * after the specified delay.
         *
         * @param delay in milliseconds
         * @param runnable the Runnable to execute.
         * @return opaque reference to the public task
         * @throws ArgumentException if runnable is null
         */
        public Object scheduleAfter(long delay,
                                    ThreadStart runnable)
        {

            if (runnable == null)
            {
                throw new ArgumentException("runnable == null");
            }

            return this.addTask(DateTime.Now.Ticks + TimeSpan.TicksPerMillisecond * delay, runnable, 0, false);
        }

        /**
         * Causes the specified Runnable to be executed once in the background
         * at the specified time.
         *
         * @param date time at which to execute the specified Runnable
         * @param runnable the Runnable to execute.
         * @return opaque reference to the public task
         * @throws ArgumentException if date or runnable is null
         */
        public Object scheduleAt(DateTime date,
                                  ThreadStart runnable)
        {

            if (date == null)
            {
                throw new ArgumentException("date == null");
            }
            else if (runnable == null)
            {
                throw new ArgumentException("runnable == null");
            }

            return this.addTask(date.Ticks, runnable, 0, false);
        }

        /**
         * Causes the specified Runnable to be executed periodically in the
         * background, starting at the specified time.
         *
         * @return opaque reference to the public task
         * @param period the cycle period
         * @param relative if true, fixed rate sheduling else fixed delay scheduling
         * @param date time at which to execute the specified Runnable
         * @param runnable the Runnable to execute
         * @throws ArgumentException if date or runnable is null, or
         *      period is <= 0
         */
        public Object schedulePeriodicallyAt(DateTime date, long period,
                                             ThreadStart runnable,
                                             bool relative)
        {

            if (date == null)
            {
                throw new ArgumentException("date == null");
            }
            else if (period <= 0)
            {
                throw new ArgumentException("period <= 0");
            }
            else if (runnable == null)
            {
                throw new ArgumentException("runnable == null");
            }

            return addTask(date.Ticks, runnable, period, relative);
        }

        /**
         * Causes the specified Runnable to be executed periodically in the
         * background, starting after the specified delay.
         *
         * @return opaque reference to the public task
         * @param period the cycle period
         * @param relative if true, fixed rate sheduling else fixed delay scheduling
         * @param delay in milliseconds
         * @param runnable the Runnable to execute.
         * @throws ArgumentException if runnable is null or period is <= 0
         */
        public Object schedulePeriodicallyAfter(long delay,
                 long period, ThreadStart runnable,
                 bool relative)
        {

            if (period <= 0)
            {
                throw new ArgumentException("period <= 0");
            }
            else if (runnable == null)
            {
                throw new ArgumentException("runnable == null");
            }

            return addTask(DateTime.Now.Ticks + TimeSpan.TicksPerMillisecond * delay, runnable, period, relative);
        }

        /**
         * Shuts down this timer after the current task (if any) completes. <p>
         *
         * After this call, the timer has permanently entered the shutdown state;
         * attempting to schedule any new task or directly restart this timer will
         * result in an  IllegalStateException. <p>
         *
         */
        public void shutdown()
        {
            lock (this)
            {
                if (!this.isShutdown)
                {
                    this.isShutdown = true;

                    this.taskQueue.cancelAllTasks();
                }
            }
        }

        /** for compatiblity with previous version */
        public void shutDown()
        {
            lock (this)
            {
                shutdown();
            }
        }

        /**
         * Shuts down this timer immediately, interrupting the wait state associated
         * with the current head of the task queue or the wait state public to
         * the currently executing task, if any such state is currently in effect.
         *
         * After this call, the timer has permanently entered the shutdown state;
         * attempting to schedule any new task or directly restart this timer will
         * result in an IllegalStateException. <p>
         *
         * <b>Note:</b> If the integrity of work performed by a scheduled task
         * may be adversely affected by an unplanned interruption, it is the
         * responsibility of the task's implementation to deal correctly with the
         * possibility that this method is called while such work is in progress,
         * for instance by catching the InterruptedException, completing the work,
         * and then rethrowing the exception.
         */
        public void shutdownImmediately()
        {
            lock (this)
            {
                if (!this.isShutdown)
                {
                    Thread runner = this.taskRunnerThread;

                    this.isShutdown = true;

                    if (runner != null && runner.IsAlive)
                    {
                        runner.Interrupt();
                    }

                    this.taskQueue.cancelAllTasks();
                }
            }
        }

        /**
         * Causes the task referenced by the supplied argument to be cancelled.
         * If the referenced task is currently executing, it will continue until
         * finished but will not be rescheduled.
         *
         * @param task a task reference
         */
        public static void cancel(Object task)
        {

            if (task is Task)
            {
                ((Task)task).cancel();
            }
        }

        /**
         * Retrieves whether the specified argument references a cancelled task.
         *
         * @param task a task reference
         * @return true if referenced task is cancelled
         */
        public static bool isCancelled(Object task)
        {
            return (task is Task) ? ((Task)task).isCancelled()
                                          : true;
        }

        /**
         * Retrieves whether the specified argument references a task scheduled
         * periodically using fixed rate scheduling.
         *
         * @param task a task reference
         * @return true if the task is scheduled at a fixed rate
         */
        public static bool isFixedRate(Object task)
        {

            if (task is Task)
            {
                Task ltask = (Task)task;

                return (ltask.relative && ltask.period > 0);
            }
            else
            {
                return false;
            }
        }

        /**
         * Retrieves whether the specified argument references a task scheduled
         * periodically using fixed delay scheduling.
         *
         * @param task a task reference
         * @return true if the reference is scheduled using a fixed delay
         */
        public static bool isFixedDelay(Object task)
        {

            if (task is Task)
            {
                Task ltask = (Task)task;

                return (!ltask.relative && ltask.period > 0);
            }
            else
            {
                return false;
            }
        }

        /**
         * Retrieves whether the specified argument references a task scheduled
         * for periodic execution.
         *
         * @param task a task reference
         * @return true if the task is scheduled for periodic execution
         */
        public static bool isPeriodic(Object task)
        {
            return (task is Task) ? (((Task)task).period > 0)
                                          : false;
        }

        /**
         * Retrieves the last time the referenced task was executed, as a
         * Date object. If the task has never been executed, null is returned.
         *
         * @param task a task reference
         * @return the last time the referenced task was executed; null if never
         */
        public static DateTime? getLastScheduled(Object task)
        {

            if (task is Task)
            {
                Task ltask = (Task)task;
                long last = ltask.getLastScheduled();

                return (last == 0) ? null
                                   : (DateTime?)new DateTime(last);
            }
            else
            {
                return null;
            }
        }

        /**
         * Sets the periodicity of the designated task to a new value. <p>
         *
         * If the designated task is cancelled or the new period is identical to the
         * task's current period, then this invocation has essentially no effect
         * and the submitted object is returned. <p>
         *
         * Otherwise, if the new period is greater than the designated task's
         * current period, then a simple assignment occurs and the submittted
         * object is returned. <p>
         *
         * If neither case holds, then the designated task is cancelled and a new,
         * equivalent task with the new period is scheduled for immediate first
         * execution and returned to the caller. <p>
         *
         * @return a task reference, as per the rules stated above.
         * @param task the task whose periodicity is to be set
         * @param period the new period
         */
        public static Object setPeriod(Object task, long period)
        {
            return (task is Task) ? ((Task)task).setPeriod(period)
                                          : task;
        }

        /**
         * Retrieves the next time the referenced task is due to be executed, as a
         * Date object. If the referenced task is cancelled, null is returned.
         *
         * @param task a task reference
         * @return the next time the referenced task is due to be executed
         */
        public static DateTime? getNextScheduled(Object task)
        {

            if (task is Task)
            {
                Task ltask = (Task)task;
                long next = ltask.isCancelled() ? 0
                                                       : ltask.getNextScheduled();

                return next == 0 ? null
                                 : (DateTime?)new DateTime(next);
            }
            else
            {
                return null;
            }
        }

        /**
         * Adds to the task queue a new Task object encapsulating the supplied
         * Runnable and scheduling arguments.
         *
         * @param first the time of the task's first execution
         * @param runnable the Runnable to execute
         * @param period the task's periodicity
         * @param relative if true, use fixed rate else use fixed delay scheduling
         * @return an opaque reference to the public task
         */
        protected Task addTask(long first, ThreadStart runnable,
                                long period, bool relative)
        {

            if (this.isShutdown)
            {
                throw new InvalidOperationException("shutdown");
            }

            Task task = new Task(this, first, runnable, period, relative);

            // sychronized
            this.taskQueue.addTask(task);

            // sychronized
            this.restart();

            return task;
        }

        /** Sets the background thread to null. */
        protected void clearThread()
        {

            lock (this)
            {
                //#endif JAVA2FULL
                taskRunnerThread = null;
            }
        }

        /**
         * Retrieves the next task to execute, or null if this timer is shutdown,
         * the current thread is interrupted, or there are no queued tasks.
         *
         * @return the next task to execute, or null
         */
        protected Task nextTask()
        {

            try
            {
                while (!this.isShutdown)
                {
                    long now;
                    long next;
                    long wait;
                    Task task;

                    // synchronized to ensure removeTask
                    // applies only to the peeked task,
                    // when the computed wait <= 0
                    lock (this.taskQueue)
                    {
                        task = this.taskQueue.peekTask();

                        if (task == null)
                        {

                            // queue is empty
                            break;
                        }

                        now = DateTime.Now.Ticks;
                        next = task._next;
                        wait = (next - now);

                        if (wait > 0)
                        {

                            // release ownership of taskQueue monitor and await
                            // notification of task addition or cancellation,
                            // at most until the time when the peeked task is
                            // next supposed to execute
                            this.taskQueue.park(wait);

                            continue;           // to top of loop
                        }
                        else
                        {
                            this.taskQueue.removeTask();
                        }
                    }

                    long period = task.period;

                    if (period > 0)
                    {           // repeated task
                        if (task.relative)
                        {    // using fixed rate shceduling
                            long late = (now - next);

                            if (late > period)
                            {

                                // ensure that really late tasks don't
                                // completely saturate the head of the
                                // task queue
                                period = 0;     /** @todo : is -1, -2 ... fairer? */
                            }
                            else if (late > 0)
                            {

                                // compensate for scheduling overruns
                                period -= late;
                            }
                        }

                        task.updateSchedule(now, now + period);
                        this.taskQueue.addTask(task);
                    }

                    return task;
                }
            }
            catch (ThreadInterruptedException )
            {

                //e.printStackTrace();
            }

            return null;
        }

        /**
         * stats var
         */
        //static int nowCount = 0;



        /**
         * The Runnable that the background thread uses to execute
         * scheduled tasks. <p>
         *
         * <b>Note:</b> Outer class could simply implement Runnable,
         * but using an inner class protects the public run method
         * from potential abuse.
         */


        /**
         * Runs the next available task in the background thread. <p>
         *
         * When there are no available tasks, the background
         * thread dies and its instance field is cleared until
         * tasks once again become available.
         */
        public void run()
        {

            try
            {
                do
                {
                    Task task = this.nextTask();

                    if (task == null)
                    {
                        break;
                    }

                    // PROBLEM: If the runnable throws an exception other
                    //          than InterruptedException (which likely stems
                    //          naturally from calling shutdownImmediately()
                    //          or getThread().interrupt()), this will still
                    //          cause the loop to exit, which is to say that
                    //          task scheduling will stop until a new task is
                    //          added or the timer is restarted directly, even
                    //          though there may still be uncancelled tasks
                    //          left on the queue.
                    //
                    // TODO:    Clarify and establish a contract regarding
                    //          the difference between InterruptedException,
                    //          RuntimeException and other things, like
                    //          UndeclaredThrowableException.
                    //
                    // SOL'N:   At present, we simply require each runnable to
                    //          understand its part of the implicit contract,
                    //          which is to deal with exceptions internally
                    //          (not throw them up to the timer), with the
                    //          possible exception of InterruptedException.
                    //
                    //          If the integrity of work performed by the
                    //          runnable may be adversely affected by an
                    //          unplanned interruption, the runnable should
                    //          deal with this directly, for instance by
                    //          catching the InterruptedException, ensuring
                    //          that some integrity preserving state is
                    //          attained, and then rethrowing the exception.
                    task.runnable.Invoke();
                } while (true);
            }
            finally
            {
                this.clearThread();
            }
        }


        /**
         * Encapsulates a Runnable and its scheduling attributes.
         *
         * Essentially, a wrapper class used to schedule a Runnable object
         * for execution by the enclosing HsqlTimer's TaskRunner in a
         * background thread.
         */
        protected class Task
        {

            /** What to run. */
            public ThreadStart runnable;

            /** The periodic interval, or 0 if one-shot. */
            public long period;

            /** The time this task was last executed, or 0 if never. */
            public long _last;

            /** The next time this task is scheduled to execute. */
            public long _next;

            /**
             * Whether to silently remove this task instead of running it,
             * the next time (if ever) it makes its way to the head of the
             * timer queue.
             */
            public bool cancelled = false;

            /** Serializes concurrent access to the cancelled field. */
            private Object cancel_mutex = new Object();

            /**
             * Scheduling policy flag. <p>
             *
             *         * wall-clock time provided by System.currentTimeMillis().  <p>
    When true, scheduling is fixed rate (as opposed to fixed delay),
             * and schedule updates are calculated relative to when the task was
             * was last run rather than a fixed delay starting from the current
              *
             * This helps normalize scheduling for tasks that must attempt to
             * maintain a fixed rate of execution.
             */
            public bool relative;

            EfzTimer _o;
            /**
             * Constructs a new Task object encapulating the specified Runnable
             * and scheduling arguments.
             *
             * @param first the first time to execute
             * @param runnable the Runnable to execute
             * @param period the periodicity of execution
             * @param relative if true, use fixed rate scheduling else fixed delay
             */
            public Task(EfzTimer _o, long first, ThreadStart runnable, long period,
                     bool relative)
            {

                this._o = _o;
                this._next = first;
                this.runnable = runnable;
                this.period = period;
                this.relative = relative;
            }

            // fixed reported race condition

            /** Sets this task's cancelled flag true and signals its taskQueue. */
            public void cancel()
            {

                bool signalCancelled = false;

                lock (cancel_mutex)
                {
                    if (!cancelled)
                    {
                        cancelled = signalCancelled = true;
                    }
                }

                if (signalCancelled)
                {
                    _o.taskQueue.signalTaskCancelled(this);
                }
            }

            /**
             * Retrieves whether this task is cancelled.
             *
             * @return true if cancelled, else false
             */
            public bool isCancelled()
            {

                lock (cancel_mutex)
                {
                    return cancelled;
                }
            }

            /**
             * Retrieves the instant in time just before this task was
             * last executed by the background thread. A value of zero
             * indicates that this task has never been executed.
             *
             * @return the last time this task was executed or zero if never
             */
            public long getLastScheduled()
            {
                lock (this)
                {
                    return _last;
                }
            }

            /**
             * Retrieves the time at which this task is next scheduled for
             * execution.
             *
             * @return the time at which this task is next scheduled for
             *      execution
             */
            public long getNextScheduled()
            {
                lock (this)
                {
                    return _next;
                }
            }

            /**
             * Updates the last and next scheduled execution times.
             *
             * @param last when this task was last executed
             * @param next when this task is to be next executed
             */
            public void updateSchedule(long last, long next)
            {
                lock (this)
                {
                    this._last = last;
                    this._next = next;
                }
            }

            /**
             * Sets the new periodicity of this task in milliseconds. <p>
             *
             * If this task is cancelled or the new period is identical to the
             * current period, then this invocation has essentailly no effect
             * and this object is returned. <p>
             *
             * Otherwise, if the new period is greater than the current period, then
             * a simple field assignment occurs and this object is returned. <p>
             *
             * If none of the previous cases hold, then this task is cancelled and
             * a new, equivalent task with the new period is scheduled for
             * immediate first execution and returned to the caller. <p>
             *
             * @param newPeriod the new period
             * @return a task reference, as per the rules stated above.
             */
            public Object setPeriod(long newPeriod)
            {
                lock (this)
                {
                    if (this.period == newPeriod || this.isCancelled())
                    {
                        return this;
                    }
                    else if (newPeriod > this.period)
                    {
                        this.period = newPeriod;

                        return this;
                    }
                    else
                    {
                        this.cancel();

                        return _o.addTask(DateTime.Now.Ticks, this.runnable, newPeriod,
                                                      this.relative);
                    }
                }
            }
        }

        /**
         * Heap-based priority queue.
         *
         * Provides extensions to facilitate and simplify implementing
         * timer functionality.
         */
        protected class TaskQueue : EfzArrayHeap
        {

            /**
             * Constructs a new TaskQueue with the specified initial capacity and
             * ObjectComparator.
             *
             * @param capacity the initial capacity of the queue
             * @param oc The ObjectComparator this queue uses to maintain its
             *      Heap invariant.
             */
            public TaskQueue(int capacity, ObjectComparator oc)
                : base(capacity, oc)
            {

            }

            /**
             * Type-safe add method. <p>
             *
             * Can be used to inject debugging or accounting behaviour. <p>
             *
             * @param task the task to add
             */
            public void addTask(Task task)
            {

                // System.out.println("task added: " + task);
                base.add(task);
            }

            /**
             * Atomically removes all tasks in this queue and then and cancels
             * them.
             */
            public void cancelAllTasks()
            {

                Object[] oldHeap;
                int oldCount;

                lock (this)
                {
                    oldHeap = this.heap;
                    oldCount = this.count;

                    // 1 instead of 0 to avoid unintended aoob exceptions
                    this.heap = new Object[1];
                    this.count = 0;
                }

                for (int i = 0; i < oldCount; i++)
                {
                    ((Task)oldHeap[i]).cancelled = true;
                }
            }

            /**
             * Causes the calling thread to wait until another thread invokes
             * {@link #unpark() unpark} or the specified amount of time has
             * elapsed.
             *
             * Implements the sync & wait(n) half of this queue's availability
             * condition. <p>
             *
             * @param timeout the maximum time to wait in milliseconds.
             * @throws java.lang.InterruptedException if another thread has
             *    interrupted the current thread.  The <i>interrupted status</i> of
             *    the current thread is cleared when this exception is thrown.
             */
            public void park(long timeout)
            {

                lock (this)
                {
                    Monitor.Wait(this, (int)(timeout / TimeSpan.TicksPerMillisecond));
                }
            }

            /**
             * Retrieves the head of this queue, without removing it. <p>
             *
             * This method has the side-effect of removing tasks from the
             * head of this queue until a non-cancelled task is encountered
             * or this queue is empty. <p>
             *
             * If this queue is initially empty or is emptied in the process
             * of finding the earliest scheduled non-cancelled task,
             * then null is returned. <p>
             *
             * @return the earliest scheduled non-cancelled task, or null if no such
             *      task exists
             */
            public Task peekTask()
            {
                lock (this)
                {
                    while (base.heap[0] != null
                            && ((Task)base.heap[0]).isCancelled())
                    {
                        base.remove();
                    }

                    return (Task)base.heap[0];
                }
            }

            /**
             * Informs this queue that the given task is supposedly cancelled. <p>
             *
             * If the indicated task is identical to the current head of
             * this queue, then it is removed and this queue is
             * {@link #unpark() unparked}. <p>
             *
             * The cancelled status of the given task is not verified; it is
             * assumed that the caller is well-behaved (always passes a
             * non-null reference to a cancelled task).
             *
             * @param task a supposedly cancelled task
             */
            public void signalTaskCancelled(Task task)
            {
                lock (this)
                {
                    // We only care about the case where HsqlTimer.nextTask
                    // might be parked momentarily on this task.
                    if (task == base.heap[0])
                    {
                        base.remove();
                        Monitor.Pulse(this);
                    }
                }
            }

            /**
             * Type-safe remove method. <p>
             *
             * Removes the head task from this queue. <p>
             *
             * Can be used to inject debugging or accounting behaviour. <p>
             *
             * @return this queue's head task or null if no such task exists
             */
            public Task removeTask()
            {

                // System.out.println("removing task...");
                return (Task)base.remove();
            }

            /**
             * Wakes up a single thread (if any) that is waiting on this queue's
             * {@link #park(long) park} method.
             *
             * Implements the sync & notify half of this queue's availability
             * condition.
             */
            public void unpark()
            {
                lock (this)
                {
                    Monitor.Pulse(this);
                }
            }
        }

    }
}


